package com.springboot.util.pdf;

import cn.hutool.json.JSONObject;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Image;
import com.itextpdf.text.pdf.PdfDocument;
import com.springboot.util.common.MyStreamUtil;
import com.springboot.util.file.MyFileUtil;
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Font;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.parser.PdfReaderContentParser;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;


/**
 * @description:
 * @author: zyj
 * @create: 2020-09-14 16:46
 **/
@Component
@Slf4j
public class ItextPdfUtils {


    private static final String PDFSIGN_TMP_PATH = "pdfutil/";
    float min = 160 * 0.75F;//高度等比缩放 160(px)


    /**
     * 方法描述 pdf 放入图片
     * @since: 1.0
     * @param fileByte pdf目标文件流
     * @param imgByte 图片流
     * @param imgType 图片类型
     * @param x 偏移量
     * @param y 偏移量
     * @return: List<MatchItem>
     * @author: zyj
     * @date: 2024/5/13
     */
    public byte[] addImg(byte[] fileByte, InputStream imgByte,String imgType, int x, int y)  {
        //创建临时文件夹
        MyFileUtil.createDir(PDFSIGN_TMP_PATH);
        //临时文件
        String tempFile = PDFSIGN_TMP_PATH + File.separator + UUID.randomUUID() + ".pdf";
        FileOutputStream outTempFile = null;
        PdfStamper stamper = null;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            outTempFile = new FileOutputStream(tempFile);
            PdfReader reader = new PdfReader(fileByte);
            // 创建PdfStamper对象以追加到现有的PDF文档
            stamper = new PdfStamper(reader,   outTempFile);
            stamper.setFormFlattening(true);

            //pdf页数
            int pdfPages = reader.getNumberOfPages();

            // 加载图像
            BufferedImage bufferedImage = ImageIO.read(imgByte);
            ImageIO.write(bufferedImage, imgType, bos);
            //此处通过hutool生成二维码图片,也可以通过上面的方式选择本地文件
//        BufferedImage generate = QrCodeUtil.
//                generate("abcdefghijklmnopqrstuvwxyz", 500, 500);
//        ImageIO.write(generate, "png", bos);
//        Image image = Image.getInstance(bos.toByteArray());


            for (int i = 1; i <= pdfPages; i++) {
                //图片处理
                Image img = Image.getInstance(bos.toByteArray());
                //设置图片大小
                img.scaleAbsolute(110, 110);
                //设置图片位置
                img.setAbsolutePosition(x, y);
                stamper.getOverContent(i).addImage(img);
            }



        } catch (Exception e) {
            throw new RuntimeException(e);
        }finally {
            //关闭资源
            try {
                stamper.close();
            } catch (Exception e) {
                log.error("MyStreamUtil stamper", e);
            }
            MyStreamUtil.close(bos);
            MyStreamUtil.close(outTempFile);
        }

        return null;
    }


    /**
     * 根据关键字和pdf路径，全文搜索关键字
     * @param fileByte pdf目标文件流
     * @param keyword 关键字
     * @return
     * @throws Exception
     */
    public List<MatchItem> matchAllByte(byte[] fileByte, String keyword) throws Exception {
        List<MatchItem> items = new ArrayList<>();
        PdfReader reader = new PdfReader(fileByte);
        //获取pdf页数
        int pageSize = reader.getNumberOfPages();
        //逐页匹配关键字
        for(int page = 1;page <= pageSize;page++){
            items.addAll(matchPage(reader,page,keyword));
        }
        return items;
    }

    /**
     * 根据关键字和pdf路径，全文搜索关键字
     * @param filePath pdf目标路径
     * @param keyword 关键字
     * @return
     * @throws Exception
     */
    public List<MatchItem> matchAllPath(String filePath, String keyword) throws Exception {
        List<MatchItem> items = new ArrayList<MatchItem>();
        PdfReader reader = new PdfReader(filePath);
        //获取pdf页数
        int pageSize = reader.getNumberOfPages();
        //逐页匹配关键字
        for(int page = 1;page <= pageSize;page++){
            items.addAll(matchPage(reader,page,keyword));
        }
        return items;
    }

    /**
     * 根据关键字、文档路径、pdf页数寻找特定的文件内容
     * @param reader
     * @param pageNumber 页数
     * @param keyword 关键字
     * @return
     * @throws Exception
     */
    public List<MatchItem> matchPage(PdfReader reader, Integer pageNumber,String keyword) throws Exception {
        PdfReaderContentParser parse = new PdfReaderContentParser(reader);
        Rectangle rectangle = reader.getPageSize(pageNumber);
        //匹配监听
        KeyWordPositionListener renderListener = new KeyWordPositionListener();
        renderListener.setKeyword(keyword);
        renderListener.setPageNumber(pageNumber);
        renderListener.setCurPageSize(rectangle);
        parse.processContent(pageNumber, renderListener);
        return findKeywordItems(renderListener,keyword);
    }

    /**
     * 找到匹配的关键词块
     * @param renderListener
     * @param keyword
     * @return
     */
    public List<MatchItem> findKeywordItems(KeyWordPositionListener renderListener,String keyword){
        //先判断本页中是否存在关键词
        List<MatchItem> allItems = renderListener.getAllItems();//所有块LIST
        StringBuffer sbtemp = new StringBuffer("");

        for(MatchItem item : allItems){//将一页中所有的块内容连接起来组成一个字符串。
            sbtemp.append(item.getContent());
        }

        List<MatchItem> matches = renderListener.getMatches();

        //一页组成的字符串没有关键词，直接return
        //第一种情况：关键词与块内容完全匹配的项,直接返回
        if(sbtemp.toString().indexOf(keyword) == -1 || matches.size() > 0){
            return matches;
        }
        //第二种情况：多个块内容拼成一个关键词，则一个一个来匹配，组装成一个关键词
        sbtemp = new StringBuffer("");
        List<MatchItem> tempItems = new ArrayList();
        for(MatchItem item : allItems){
            if(keyword.indexOf(item.getContent()) != -1 ){
                tempItems.add(item);
                sbtemp.append(item.getContent());

                if(keyword.indexOf(sbtemp.toString()) == -1){//如果暂存的字符串和关键词 不再匹配时
                    sbtemp = new StringBuffer(item.getContent());
                    tempItems.clear();
                    tempItems.add(item);
                }

                if(sbtemp.toString().equalsIgnoreCase(keyword)){//暂存的字符串正好匹配到关键词时
                    matches.add(tempItems.get(0));//得到匹配的项
                    sbtemp = new StringBuffer("");//清空暂存的字符串
                    tempItems.clear();//清空暂存的LIST
                    continue;//继续查找
                }
            }else{//如果找不到则清空
                sbtemp = new StringBuffer("");
                tempItems.clear();
            }
        }
        return matches;
    }


    /**
     * 替换目标文字，生成新的pdf文件
     * @param src 目标pdf流
     * @param dest 新pdf的路径
     *        offectX x偏移量
     *        offectY y偏移量
     * @throws Exception
     */
    public void manipulatePdfByte(byte[] src,String dest,List<MatchItem> matchItems,String keyWord,String keyWordNew, int offsetX, int offsetY, float fontSize) throws Exception{
        PdfReader reader = new PdfReader(src);
        manipulatePdf(reader ,dest, matchItems, keyWord, keyWordNew, offsetX, offsetY, fontSize);
    }


    /**
     * 替换目标文字，生成新的pdf文件
     * @param path 目标pdf路径
     * @param dest 新pdf的路径
     *        offectX x偏移量
     *        offectY y偏移量
     * @throws Exception
     */
    public void manipulatePdfPath(String path,String dest,List<MatchItem> matchItems,String keyWord,String keyWordNew, int offectX, int offectY, float fontSize) throws Exception{
        PdfReader reader = new PdfReader(path);
        manipulatePdf(reader ,dest, matchItems, keyWord, keyWordNew, offectX, offectY, fontSize);
    }

    /**
     * 替换目标文字，生成新的pdf文件
     * @param reader 目标pdf
     * @param dest 新pdf的路径
     *        offectX x偏移量
     *        offectY y偏移量
     * @throws Exception
     */
    public void manipulatePdf(PdfReader reader ,String dest,List<MatchItem> matchItems,String keyWord,String keyWordNew, int offectX, int offectY, float fontSize) throws Exception{
        PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
        PdfContentByte canvas = null;
        Map<Integer,List<MatchItem>> mapItem = new HashMap<Integer,List<MatchItem>>();
        List<MatchItem> itemList = null;
        for(MatchItem item : matchItems){
            Integer pageNum = item.getPageNum();
            if(mapItem.containsKey(pageNum)){
                itemList = mapItem.get(pageNum);
                itemList.add(item);
                mapItem.put(pageNum,itemList);
            }else{
                itemList = new ArrayList<MatchItem>();
                itemList.add(item);
                mapItem.put(pageNum,itemList);
            }
        }
        //遍历每一页去修改
        for(Integer page : mapItem.keySet()){
            List<MatchItem> items = mapItem.get(page);
            //遍历每一页中的匹配项
            for(MatchItem item : items){
                canvas = stamper.getOverContent(page);
                float x = item.getX() + offectX;
                float y = item.getY() + offectY;
                float fontWidth = item.getFontWidth();
                float fontHeight = item.getFontHeight();
                canvas.saveState();
                canvas.setColorFill(BaseColor.WHITE);
                canvas.rectangle(x, y,fontWidth*keyWord.length(),fontSize);
                canvas.fill();
                canvas.restoreState();
                //开始写入文本
                canvas.beginText();
                BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED);
                Font font = new Font(bf,fontSize,Font.BOLD);
                //设置字体和大小
                canvas.setFontAndSize(font.getBaseFont(), fontSize);
                //设置字体的输出位置
                canvas.setTextMatrix(x, y+fontWidth/10);
                //要输出的text
                canvas.showText(keyWordNew);

                canvas.endText();
            }
        }
        stamper.close();
        reader.close();
    }

    /**
     * 替换pdf中指定文字
     * @param src 目标pdf路径
     * @param dest 新pdf的路径
     * @param keyWord 替换的文字
     * @param keyWordNew 替换后的文字
     * @throws Exception
     */
    public void pdfReplace(String src,String dest,String keyWord,String keyWordNew, float fontSize) throws Exception{
        manipulatePdfPath(src,dest,matchAllPath(src,keyWord),keyWord,keyWordNew,0, 0, fontSize);
    }

    /**
    * @Description: 单文字替换
    * @Param: [src, keyWord, keyWordNew]
    * @return: byte[]
    * @Author: zyj
    * @Date: 2020/9/14
    */
    public byte[] pdfReplaceByte(byte[] src, String keyWord, String keyWordNew, String dictonary, String fileName, int offsetX, int offsetY, float fontSize) throws Exception {
        /**step1: 创建文件夹*/
        String dictionary = File.separator+dictonary;
        boolean b = MyFileUtil.createDir(dictionary);
        if(!b){
            return null;
        }

        /**step2: 创建文件*/
        String path = dictionary+File.separator+fileName;
        manipulatePdfByte(src, path, matchAllByte(src,keyWord),keyWord,keyWordNew,offsetX, offsetY, fontSize);


        /**step3: 获取文件流*/
        byte[] fileByte = MyFileUtil.toByteArray(path);

        /**step4: 删除临时文件*/
        MyFileUtil.deleteFile(path);

        return fileByte;
    }

    /**
     * description: pdf 放入图片
     * 1 pt = px * 96/72 = px * 4/3
     * 1 pt = mm * 72/25.4
     *
     * @param pdf  pdf文件
     * @param imge 图片
     * @param json 关键字json格式 { "KW":"图片：", "XOFFSET":"130", "YOFFSET":"-40" }
     * @return: byte 重新生成的文件
     * @author: zyj
     * @date: 2022/2/21
     */
    public byte[] saveImgePdf(byte[] pdf, byte[] imge, JSONObject json) {
        if (pdf.length <= 0 || imge.length <= 0 || json == null) {
            return null;
        }
        String splitParentkeyValues = json.getStr("KW");
        Float absoultX = json.getFloat("XOFFSET");
        Float absoultY = json.getFloat("YOFFSET");

        //创建临时文件夹
        MyFileUtil.createDir(PDFSIGN_TMP_PATH);
        //临时文件
        String tempFile = PDFSIGN_TMP_PATH + File.separator + UUID.randomUUID() + ".pdf";
        byte[] fileByte = null;
        byte[] newFileByte = MyStreamUtil.byteCopy(pdf);


        PdfStamper stamp = null;
        PdfReader pdfReaders = null;
        FileOutputStream fileOutputStream = null;
        try {
             /*sourcePDF: pdf的文档路径
            splitParentkeyValues[i]:关键字，其中对关键字进行特殊符号的过滤，不然会导致后面的匹配结果有误。*/
            List<MatchItem> matches = this.matchAllByte(pdf, splitParentkeyValues);
            //复制pdf进行修改
            pdfReaders = new PdfReader(newFileByte, "PDF".getBytes());
            fileOutputStream = new FileOutputStream(tempFile);
            stamp = new PdfStamper(pdfReaders, fileOutputStream);

            for (int j = 0; j < matches.size(); j++) {
                MatchItem matchItemNo = matches.get(j);
                Float fontSize = matchItemNo.getFontSize();
                int pageNum = matchItemNo.getPageNum();
                // 获取操作的页面
                PdfContentByte overContent = stamp.getOverContent(pageNum);
                // 插入水印
                Image img = Image.getInstance(imge);
                float sealWidth;
                float sealHeight;
                float height = img.getScaledHeight();
                float width = img.getScaledWidth();
                if (height < width) {
                    sealHeight = min;
                    sealWidth = min * (width / height);
                } else {
                    sealWidth = min;
                    sealHeight = min * (height / width);
                }
                // 设置图片宽高
                img.scaleToFit(sealWidth, sealHeight);
                /*找出关键字后，将要盖章的图片准确定位到关键字周围，也可以采用坐标的方式**/
                //x轴以关键字中心点为原点
                String content = matchItemNo.getContent();
                float x = matchItemNo.getX() - content.length() * fontSize / 2 + absoultX;
                //y轴以图片中心点为原点
                float y = matchItemNo.getY() - sealHeight / 2 + absoultY;
                log.info("x:{} y:{}", x, y);

                // 位置左边距、底边距
                img.setAbsolutePosition(x, y);
                overContent.addImage(img);
            }

        } catch (Exception e) {
            log.error("saveImgePdf 转换失败", e);
        } finally {
            try {
                if (stamp != null) {
                    stamp.close();
                }
                if (pdfReaders != null) {
                    pdfReaders.close();
                }
                if (fileOutputStream != null) {
                    fileOutputStream.close();
                }
                fileByte = MyFileUtil.toByteArray(tempFile);

                //删除临时文件
                MyFileUtil.deleteFile(tempFile);
            } catch (Exception e) {
                log.error("saveImgePdf关闭失败", e);
            }
        }

        return fileByte;
    }

    public static void main(String[] args) {
        ItextPdfUtils itextPdfUtils = new ItextPdfUtils();
        try {
            //itextPdfUtils.pdfReplace("D:\\Download\\2.pdf", "D:\\Download\\22222"+System.currentTimeMillis()+".pdf", "123123123123", "哇哈哈哈", 20f);
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("KW", "123123123123");
            jsonObject.put("XOFFSET", "0");
            jsonObject.put("YOFFSET", "0");

            MyFileUtil.createFile(itextPdfUtils.saveImgePdf(MyFileUtil.toByteArray("D:\\Download\\2.pdf"), MyFileUtil.toByteArray("D:\\Download\\1712915411527.jpg"), jsonObject),
                    "D:\\Download", "222223"+System.currentTimeMillis()+".pdf");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
